package org.fjsei.yewu.resolver;

import com.gauck.sam.Utilities.Utilities;
import com.gauck.sam.Utilities.model.Bigetly;
import md.cm.flow.ApprovalStmRepository;
import md.cm.unit.Division;
import md.cm.unit.DivisionRepository;
import md.cm.unit.Unit;
import md.cm.unit.Units;
import md.specialEqp.Eqp;
import md.specialEqp.RegState_Enum;
import md.specialEqp.UseState_Enum;
import md.specialEqp.inspect.DetailRepository;
import md.specialEqp.type.*;
import md.system.User;
import md.system.UserRepository;
import org.fjsei.yewu.input.DeviceCommonInput;
import org.fjsei.yewu.security.JwtUser;
import org.fjsei.yewu.util.Tool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.repository.CrudRepository;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import jakarta.annotation.PostConstruct;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * 接口利用的公共工具部分; 仅在后端利用的=非graphQL直接的接口函数。
 * 派生类必须是Spring管理的Bean=支持@Autowired逻辑。
 * 不要在graphql模型文件 定义接口 checkAuth: User，报错;
*/
public class Comngrql {
    final Map<String, String> gIdTypeChangeMap= new HashMap<>();
    /**必须是这个数据库的=主数据库？
     * 有可能的：多个数据源链接啊。
     * */
    @PersistenceContext(unitName = "entityManagerFactorySei")
    protected  EntityManager emSei;
    @Autowired protected UserRepository userRepository;
    @Autowired protected Units units;
    @Autowired protected DivisionRepository divisionRepository;
    @Autowired protected  ApprovalStmRepository approvalStms;

    @PostConstruct
    private void init() {
        gIdTypeChangeMap.put("EqpEs","Eqp");
        gIdTypeChangeMap.put("Elevator","Eqp");
        gIdTypeChangeMap.put("Amusement","Eqp");
        gIdTypeChangeMap.put("Boiler","Eqp");
        gIdTypeChangeMap.put("Crane","Eqp");
        gIdTypeChangeMap.put("FactoryVehicle","Eqp");
        gIdTypeChangeMap.put("Pipeline","Eqp");
        gIdTypeChangeMap.put("Ropeway","Eqp");
        gIdTypeChangeMap.put("Vessel","Eqp");
    }

    //验证是否登录，谁登录，有那些角色的。后端自己用的，前端不可以调用。
    public User checkAuth() {
        /*
        获取servlet路径来鉴定权限。 和ROLE_xxx交叉了，又是接口安全域又是角色，多关联关系？复合主码＋? 接口Path+UserName。
        简化做法： 直接从ROLE_字符串区分安全域接口。 ROLE_Outer_xx; ROLE_Main_xx 复合型的权限代码;每个接口默认ROLE_都是唯一性差异来区分。
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String servletPath =request.getServletPath();
        */
        Authentication auth= SecurityContextHolder.getContext().getAuthentication();
        if(auth==null)  return null;
        //graphql这块即时没登录也会有系统角色ROLE_ANONYMOUS，奇葩！
        Object principal=auth.getPrincipal();
        if(principal instanceof JwtUser){
            UUID userid=((JwtUser) principal).getId();
            //   UserDetails userDetails = jwtUserDetailsService.loadUserByUsername(name);
            //Long userId=Long.valueOf(12);
 //           return userRepository.findById(userid).orElse(null);
            return userRepository.findByUsername(((JwtUser) principal).getUsername());
        }else if(principal instanceof org.springframework.security.core.userdetails.User){
            String username=((org.springframework.security.core.userdetails.User) principal).getUsername();
            return userRepository.findByUsername(username);
        }else{
            //没有登录的也Authenticated! 有anonymousUser 有ROLE_ANONYMOUS；
            return null;
        }
    }
    //把输入的单位ID转成实体对象。没有的就报错！ Java 方法的参数可以有默认值吗？
    /**从前端搜索或直接提供的泛指单位ID映射Unit;
     * @param desu 这里单位ID有三种可能性：CompanyEs,PersonEs,Unit的模型对应的ID。
     * */
    public Unit fromInputUnitGlobalID(String desu, boolean failAssert) {
        Unit unitDesu=null;
        if(StringUtils.hasText(desu)) {
            Tool.ResolvedGuuid gId= Tool.fromGluuId(desu);
            if(gId.getType().equals("CompanyEs") )
                unitDesu= units.findUnitByCompany_Id(gId.getId());
            else if(gId.getType().equals("PersonEs") )
                unitDesu= units.findUnitByPerson_Id(gId.getId());
            else
                unitDesu= units.findById(gId.getId()).orElse(null);
            if(failAssert)
                Assert.isTrue(unitDesu != null,"未找到Unit:"+desu);
        }
        return unitDesu;
    }
    public Unit fromInputUnitGlobalID(String desu) {
        return fromInputUnitGlobalID(desu,true);
    }
    //把输入的部门ID转成实体对象。
    public Division fromInputDivisionGlobalID(String desDivi,boolean failAssert) {
        Division division=null;
        //不是纯粹空格的字符串，代表有ID输入的。
        if(StringUtils.hasText(desDivi)) {
            Tool.ResolvedGuuid gId= Tool.fromGluuId(desDivi);
            division=divisionRepository.findById(gId.getId()).orElse(null);
            if(failAssert)
                Assert.isTrue(division != null,"未找到Division:"+desDivi);
            }
        return division;
    }
    public Eqp newEQP导入(String drtype, DeviceCommonInput inp) {
        //todo: 自己库Keys已经存在的可能
        Eqp eqp=new Eqp();
        Bigetly  ol=null;
        if("cod".equals(drtype) && StringUtils.hasText(inp.getCod())){
            ol=(Bigetly) Utilities.getIt(1,"EQP_COD='"+inp.getCod()+"'");
            Assert.isTrue(null==ol.getErrorCode(),"未找到:"+inp.getCod());
        }
        else if("oid".equals(drtype) && StringUtils.hasText(inp.getOid())){
            ol=(Bigetly) Utilities.getIt(1,"OIDNO='"+inp.getOid()+"'");
            Assert.isTrue(null==ol.getErrorCode(),"未找到:"+inp.getOid());
        }
        Assert.isTrue(StringUtils.hasText(ol.getEqpType()),"EQP_TYPE空");
        String type=ol.getEqpType().substring(0,0);

        Eqp.EqpBuilder<?, ?>  eqpBld=null;
        if(type.equals("3"))
            eqpBld = Elevator.builder().flo(inp.getFlo());
        else if(type.equals("2") || type.equals("R"))
            eqpBld = Vessel.builder().pnum(inp.getFlo());
        else if(type.equals("1"))
            eqpBld = Boiler.builder();
        else if(type.equals("4"))
            eqpBld = Crane.builder();
        else if(type.equals("5"))
            eqpBld = FactoryVehicle.builder();
        else if(type.equals("6"))
            eqpBld = Amusement.builder();
        else if(type.equals("8"))
            eqpBld = Pipeline.builder();
        else if(type.equals("9"))
            eqpBld = Ropeway.builder();
        else
            eqpBld = Eqp.builder();
        //?新旧type不统一;
        String sort="", vart="", subv="";
        if(StringUtils.hasText(ol.getEqpSort()))   sort=ol.getEqpSort().substring(0,1);
        if(StringUtils.hasText(ol.getEqpVart()))   vart=ol.getEqpVart().substring(0,2);
        if(StringUtils.hasText(ol.getSubEqpVart()))   subv=ol.getSubEqpVart().substring(0,3);
        //Integer.parseInt(ol.getEqpRegSta()); [ Integer.parseInt(ol.getEqpRegSta()) ]
        eqp =eqpBld.cod(ol.getEqpCod()).type(type).oid(ol.getOidno())
                .reg(RegState_Enum.values()[ol.getEqpRegSta()])
                .ust(UseState_Enum.values()[ol.getEqpUseSta()]).sort(sort).vart(vart).subv(subv)
                .build();

        return eqp;
    }

    /** 关键表ID的类型是idType;
     * 考虑到分布式数据库：大表分片，也有可能需要注入数据库的分片字段的已经提供的数值的，分片字段不是id字段。DB分片字段的数据类型也有多样性。
     * 只有一个的 nodeInput.emSei：只能是获取 主数据库中的模型 表；
     * 还没有实例化啊，所以无法提前获取Class<T> ,Class<ID>;
     * [尝试失败]必须手动注入 T, ID的实际类型。
     * 必须是主数据库的实体表
     * */
    public <T>  T  entityOf(String id,Class<T> classType,Class<?> idType) {
        if(StringUtils.hasText(id)) {
            if (idType.equals(UUID.class)) {
                Tool.ResolvedGuuid gId =  Tool.fromGluuId(id);
                UUID sid = gId.getId();
                String stype = gId.getType();
                if(StringUtils.hasText(stype) ) {
                    String  changeTp= gIdTypeChangeMap.get(stype);
                    if(null!=changeTp)    stype=changeTp;
                    //classType和stype没有严格验证？ stype很可能无效的！
                    String clsName=classType.getSimpleName();
                    Assert.isTrue(clsName.equals(stype),"非该模型");
                    T ent = emSei.find(classType, sid);
                    return ent;
                }
            } else {
                //Tool.ResolvedGlobalId gId = Tool.fromGlobalId(id);  // && StringUtils.hasText(sid)
                return null;
                // ?  ent = emSei.find(classType, sid);
            }
        }
        return null;
    }
    /**多数都是UUID作为ID类型的；
     * <T>  T  实际是依据函数的实际使用该函数的代码所体现的返回值cast类型匹配得到的T;
     *改了ID类型了Provided id of the wrong type for class md.system.User. Expected: class java.util.UUID, got class java.lang.Long
     * 旧的使用 Long类型做ID
     * */
    public <T>  T  entityOf(String id,Class<T> classType) {
        //T  ent= nodeInput.emSei.find( classType, Long.valueOf(id) );
        return  entityOf(id,classType, UUID.class);
    }

}

